package com.gateway.config

import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException
import com.alibaba.csp.sentinel.slots.block.flow.FlowException
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException
import com.alibaba.csp.sentinel.slots.system.SystemBlockException
import com.yfree.config.YConfig
import com.yfree.global.YController
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler
import org.springframework.cloud.gateway.filter.GlobalFilter
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.reactive.CorsWebFilter
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
import java.nio.charset.StandardCharsets

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
class GatewayConfiguration : YController(), ErrorWebExceptionHandler {
    @ResponseBody
    @ExceptionHandler(Exception::class)
    fun error(e: Exception) = yGetResponseByException(e)

    @Order(Ordered.HIGHEST_PRECEDENCE)
    override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> {
        val response = exchange.response
        response.headers.add("Content-Type", "application/json; charset=${StandardCharsets.UTF_8.name()}")
        val message: String
        if (YConfig.DEBUG)
            when (ex.javaClass) {
                FlowException::class.java, DegradeException::class.java, ParamFlowException::class.java -> {
                    response.statusCode = HttpStatus.TOO_MANY_REQUESTS
                    message = "Gateway:熔断:接口限流"
                }

                SystemBlockException::class.java -> {
                    response.statusCode = HttpStatus.NOT_ACCEPTABLE
                    message = "Gateway:熔断:触发系统保护规则"
                }

                AuthorityException::class.java -> {
                    response.statusCode = HttpStatus.UNAUTHORIZED
                    message = "Gateway:熔断:授权规则不通过"
                }

                else -> {
                    response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR
                    message = "Gateway:${ex.localizedMessage}"
                }
            }
        else {
            response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR
            message = "Gateway:${if (YConfig.DEBUG) ex.localizedMessage else YConfig.Response.yErrorDefaultMessage}"
        }
        return response.writeWith(Mono.just(response.bufferFactory().wrap(yResult(code = response.statusCode!!.value(), message = message).toByteArray())))
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    fun sentinelGatewayFilter(): GlobalFilter = SentinelGatewayFilter()

    //Sentinel
    @Bean
    fun sentinelResourceAspect(): SentinelResourceAspect = SentinelResourceAspect()

    @Bean
    fun corsWebFilter(): CorsWebFilter {
        val corsConfiguration = CorsConfiguration()
        corsConfiguration.addAllowedHeader("*")
        corsConfiguration.addAllowedMethod("*")
        corsConfiguration.addAllowedOrigin("*")
        corsConfiguration.allowCredentials = true
        val source = UrlBasedCorsConfigurationSource()
        source.registerCorsConfiguration("/**", corsConfiguration)
        return CorsWebFilter(source)
    }
}
